<template>
	<view class="m-danmuku">
		<block v-for="(item,index) in dumus" :key="index">
			<view class="dumuku-item" :id="item.id" :data-duration="item.duration" :data-trackindex="item.trackIndex"
				:style="{
		          top: `${item.top}`,
		          'animation-duration': `${Number(item.duration)}s`}" @animationend="handleEnd(item.id, item.trackIndex)">
				<slot v-if="isSlot" name="danmuItem" :danmu="item"></slot>
				<view v-else class="dumu-contoent">
					{{item.content}}
				</view>
			</view>
		</block>
		<!-- 计算每条弹幕实际的宽度 -->
		<view class="bullet-container">{{currentBullet}}</view>
	</view>
</template>


<script setup>
import { nextTick, onMounted, onUnmounted, ref, getCurrentInstance } from 'vue';

const instance = getCurrentInstance()

	let cycle;

	function rpx2px(rpx) {
		return rpx / 750 * wx.getWindowInfo().windowWidth
	}
	const props = defineProps({
		/**
		 * 弹幕列表数据
		 */
		dumuList: {
			type: Array,
			required: true,
			default: () => []
		},
		/*
		 * 弹幕总数量
		 */
		pageTotal: {
			type: Number,
			required: true,
			default: 0
		},
		/**
		 * 是否循环播放
		 */
		isLoop: {
			type: Boolean,
			default: false
		},
		
		/**
		 *  弹幕速度，单位为px/s，默认为100px/s，即1s移动100px。
		 */
		speed: {
			type: Number,
			default: 100
		},
		/**
		 * 弹幕刷新频率(ms)
		 */
		debounce: {
			type: Number,
			default: 500,
		},
		/**
		 *  轨道高度，单位为rpx，默认为100rpx。
		 */
		trackHeight: {
			type: Number,
			default: 100
		},
		/**
		 * 是否开启弹幕插槽，默认否
		 */
		isSlot: {
			type: Boolean,
			default: false
		},
		/**
		 * 是否自动播放
		 */
		autoplay: {
			type: Boolean,
			default: true,
		},
		/**
		 * 弹幕垂直间距 
		 */
		top: {
			type: Number,
			default: 20,
		},
		/**
		 * 是否开启随机轨道注入弹幕
		 */
		randomChannel: {
			type: Boolean,
			default: false,
		},
	})
	
	const dumus = ref([])
	const currentBullet = ref('')
	const targetRight = ref(0)
	const tracks = ref([])
	const bullets = ref([])
	const bulletInfo = ref({})
	const i = ref(0)
	const targetW = ref(0)
	const targetH = ref(0)
	
	onMounted(() => {
		clearFn()
		if (props.autoplay) {
			startFn()
		}
		const query = uni.createSelectorQuery().in(instance.proxy)
		query.select('.m-danmuku').boundingClientRect(data => {
			targetRight.value = data.right
			targetW.value = data.width
			targetH.value = data.height
			const trackNum = Math.floor(targetH.value / rpx2px(props.trackHeight))
			// 初始时设置轨道为空闲
			tracks.value = new Array(trackNum).fill('idle')
			bullets.value = new Array(trackNum).fill([])
		}).exec()
	})
	
	onUnmounted(() => {
		clearFn()
	})
	
	const clearFn = () => {
		cycle && (clearInterval(cycle))
		dumus.value = []
		i.value = 0
	}
	
	defineExpose({
		clearFn
	})
	
	const startFn = () => {
			cycle = setInterval(async () => {
				if (i.value < props.pageTotal) {
					if (props.dumuList[i.value]) {
						// 等待弹幕添加完成后再递增i
						await pushFn(props.dumuList[i.value])
						i.value++
					}
				} else {
					if (props.isLoop) {
						i.value = 0
					} else {
						cycle && clearInterval(cycle)
					}
				}
			}, props.debounce)
		}
		
		const pushFn = (item) => {
		    return new Promise((resolve) => {
		        const tryAdd = async () => {
		            currentBullet.value = item.content
		            nextTick(() => {
		                let duration
		                const query = uni.createSelectorQuery().in(instance.proxy)
		                query.select('.bullet-container').boundingClientRect(async el => {
		                    if (el) {
		                        duration = (targetW.value + el.width) / props.speed
		                        bulletInfo.value = {
		                            width: el.width,
		                            duration
		                        }
		
		                        const trackIndex = await getTrackIndex();
		                        
		                        if (trackIndex > -1) {
		                            const trackTop = trackIndex * rpx2px(props.trackHeight) + rpx2px(props.top) + 'px'
		                            const id = 's' + Math.random().toString(36).substring(2)
		                            
		                            if (bullets.value[trackIndex].length) {
		                                bullets.value[trackIndex].push({ id })
		                            } else {
		                                bullets.value[trackIndex] = [{ id }]
		                            }
		
		                            dumus.value.push({
		                                id,
		                                top: trackTop,
		                                trackIndex,
		                                duration,
		                                ...item
		                            })
		                            
		                            resolve() // 成功添加
		                        } else {
		                            // 没有可用轨道，等待100ms后重试
		                            setTimeout(tryAdd, 100)
		                        }
		                    } else {
		                        resolve() // 出错也继续，避免阻塞
		                    }
		                }).exec();
		            })
		        }
		        
		        tryAdd()
		    })
		}
	
	const getTrackIndex = () => {
	    return new Promise(resolve => {
	        let readyIdxs = [];
	        let index = -1;
	         
	        // 优先去 idle 状态
	        tracks.value.forEach((v, idx) => v === 'idle' && readyIdxs.push(idx))
	        if (readyIdxs.length) {
	            if (props.randomChannel) {
	                const random = parseInt(Math.random() * (readyIdxs.length))
	                index = readyIdxs[random];
	            } else {
	                index = readyIdxs[0]
	            }
	            tracks.value[index] = 'running'
	            resolve(index);
	            return;
	        }
	        
	        // 没有轨道空闲，检查各轨道是否可以插入
	        let checkedCount = 0;
	        let foundTrack = -1;
	        
	        for (let i = 0; i < bullets.value.length; i++) {
	            const len = bullets.value[i].length;
	            if (len) {
	                const item = bullets.value[i][len - 1];
	                checkTrack(item.id, (flag) => {
	                    checkedCount++;
	                    if (flag && foundTrack === -1) {
	                        foundTrack = i; // 找到可用轨道
	                    }
	                    
	                    // 所有轨道检查完成
	                    if (checkedCount === bullets.value.filter(track => track.length > 0).length) {
	                        if (foundTrack !== -1) {
	                            tracks.value[foundTrack] = 'running';
	                            resolve(foundTrack);
	                        } else {
	                            resolve(-1); // 所有轨道都不可用
	                        }
	                    }
	                })
	            } else {
	                // 这个轨道是空的，应该在上面 idle 检查中已经处理了
	                checkedCount++;
	            }
	        }
	        
	        // 如果所有轨道都是空的（理论上不会发生，因为上面已经检查过idle）
	        if (bullets.value.filter(track => track.length > 0).length === 0) {
	            resolve(-1);
	        }
	    })
	}
	
	const checkTrack = (id, cb) => {
		const query = uni.createSelectorQuery().in(instance.proxy)
		query.select('#' + id).boundingClientRect(el => {
			if (el) {
				// 轨道中最后一个元素尚未完全进入展示区域，直接跳出
				if (el.right > targetRight.value) {
					cb(false)
				} else if (el.right < targetRight.value) {
					// 轨道内最后一条弹幕和新弹幕的追及问题
					// 轨道中最后一个元素已完全进去展示区域
					// 速度相同，只要初始条件满足即可，不用去考虑追及问题
					if (props.speed) {
						cb(true)
					} else {
						// this.compare(itemPos, index, len)
						// 原弹幕速度
						const v1 = (targetW.value + el.width) / +el.dataset.duration;
						/**
						 * 新弹幕
						 * s2：全程距离
						 * t2：全程时间
						 * v2：速度
						 */
						const s2 = targetW.value + bulletInfo.value.width;
						const t2 = bulletInfo.value.duration;
						const v2 = s2 / t2
		
						if (v2 <= v1) {
							cb(true)
						} else {
							// t = s / v  比较时间：t1, t2
		
							// 原弹幕跑完剩余全程所需要的时间
							const t1 = (el.right - 0) / v1;
							// 新弹幕头部跑完全程所需要的时间
							const t2 = targetW.value / v2;
							if (t2 < t1) {
								cb(false)
							}
						}
					}
				}
			}
		
		}).exec()
	}
	
	const emits = defineEmits(['loadMore'])
	
	const handleEnd = (id, trackIndex) => {
		emits('loadMore')
		bullets.value[trackIndex] = bullets.value[trackIndex]?.filter(v => v.id !== id)
		if (!bullets.value[trackIndex].length) {
			tracks.value[trackIndex] = 'idle'
		}
	}
</script>

<style lang="scss" scoped>
	.m-danmuku {
		position: relative;
		z-index: 3;
		width: 100%; // 弹幕区域的宽度为屏幕的宽度
		height: 100%; // 这里的高度可以根据实际需要显示弹幕的行数来设定，如每行高度为24px,那么我们可以设置大于

		.dumuku-item {
			position: absolute;
			left: 0;
			color: #fff;
			white-space: nowrap;
			animation: mymove 10s linear forwards;

			.dumu-contoent {
				background: #0000004d;
				border-radius: 36rpx;
				padding: 16rpx;
			}

		}

		.bullet-container {
			position: absolute;
			right: 9999rpx;
			visibility: hidden;
			padding: 16rpx;
			white-space: nowrap;
			box-sizing: border-box;
		}
	}

	@keyframes mymove {
		from {
			transform: translateX(100vw);
		}

		to {
			transform: translateX(-100%);
		}
	}
</style>